#include "treegen3d.h"
#include "math.h"
#include "3DAlgo/shadow3d.h"
#include "spacecol3d.h"
#include "common.h"
using namespace std;

TreeGen3D::TreeGen3D(Renderer * rend) : TreeGen()
{
    m_mesh = NULL;
    render = rend;
}

TreeGen3D::TreeGen3D(): TreeGen(){
    m_mesh = NULL;
}

//this method also increments the season
void TreeGen3D::calculateBranchDiameter(Bud *b){
    float d = 0;
    b->season += 1;

    //barkGenerator.ageBark(b->the_bark);
    //barkGenerator.barkToTexture(b->the_bark);

    if(b->term){
        this->calculateBranchDiameter(b->term);
        d += pow(b->term->diameter,DIAMETER_RATE  );
    }
    else
        d+=BRANCH_START_DIAMETER;

    if(b->aux){
        this->calculateBranchDiameter(b->aux);
        d+= pow(b->aux->diameter,DIAMETER_RATE  );
    }
    else
        d+=BRANCH_START_DIAMETER;

    b->diameter = pow(d, 1.0/DIAMETER_RATE);


}
void TreeGen3D::genTree(float2 pos){
    treeRoot->start = float3(pos.x,0,pos.y);
    treeRoot->end = float3(pos.x,15,pos.y);
    treeRoot->aux = NULL;
    treeRoot->term = NULL;
    treeRoot->theta = 0;
    treeRoot->phi = 0;
    treeRoot->diameter = 1;
    treeRoot->direction = float3(0,1,0);
    initializeBud(treeRoot);
}

void TreeGen3D::stupidGen(Bud *b, int depth){
    if(depth <5){
        b->aux = new Bud();
        createBudFromBud(b, b->aux);

        b->aux->end = b->auxDir*6 + b->aux->start;
        b->aux->diameter = .3;

        initializeBud(b->aux);

        b->term = new Bud();
        createBudFromBud(b,b->term);
        b->term->end = b->direction*6 + b->end;
        b->term->diameter = .3;
        initializeBud(b->term);

        stupidGen(b->aux, depth +1);
        stupidGen(b->term, depth +1);
    }
}

void TreeGen3D::auxdirTestGen(Bud *b, int depth){
    if(depth< 50){
        b->aux = new Bud();
        b->aux->start = treeRoot->start;
        b->aux->end = 20 * treeRoot->auxDir;
        initializeBud(treeRoot);
        initializeBud(b->aux);
        auxdirTestGen(b->aux, depth +1);
    }
}

void TreeGen3D::initializeBud(Bud *b){
    float3 dir(b->end.x - b->start.x, b->end.y - b->start.y, b->end.z - b->start.z);
    b->length = dir.getMagnitude();
    dir.normalize();

    b->branchID = this->branchIdGen;
    this->branchIdGen++;

    b->theta = -atan2(dir.z, dir.x);
    b->phi = -acos(dir.y);

    b->direction = dir;

    double ttheta = (rand() % 100 /100.0) * 2.0*PI;
    //random direction with aux angle about y-axis
    float3 adir(sin(AUX_ANGLE)*cos(ttheta),
                cos(AUX_ANGLE),
                sin(AUX_ANGLE)*sin(ttheta));
    //now rotate using phi and theta of direction:
    double xp = adir.x * cos(b->phi) - adir.y * sin(b->phi);
    double yp = adir.x * sin(b->phi) + adir.y * cos(b->phi);
    double zp = adir.z;
    adir.x = zp*sin(b->theta) + xp * cos(b->theta);
    adir.y = yp;
    adir.z = zp * cos(b->theta) - xp*sin(b->theta);

    adir.normalize();
    b->auxDir = adir;
    b->darken = max((double)(rand()%100) /100.0, 0.7);
    numBuds ++;
    b->spaceColIndex = numBuds;
    b->term_fate = DORMANT;
    b->aux_fate = DORMANT;
    b->rotleaf = (float) (rand() %100) /100.0 *2.0*PI;
    b->season = 0;
        barkGenerator.initBark(b->the_bark);
        barkGenerator.barkToTexture(b->the_bark);

}
Bud* TreeGen3D::findBud(int index){
    Bud *targetBud = NULL;
    findBudHelper(this->treeRoot, index, &targetBud);
    //assert(targetBud != NULL);
    if (targetBud == NULL){
        //cout << "NULL BUD" << endl;
        return NULL;
    }
    return targetBud;

}

void TreeGen3D::findBudHelper(Bud* curbud, int index, Bud **targetBud){
    if (curbud->branchID == index){
        *targetBud = curbud;
        return;
    }
    if (curbud->aux){
        findBudHelper(curbud->aux, index, targetBud);
    }
    if(curbud->term){
        findBudHelper(curbud->term, index, targetBud);
    }

}
void TreeGen3D::iterateShadowB(Shadow3D *shadowGrid){
   //for(int i =0; i< 10; i++)
    {
        //shadowGrid->populateGrid(this->treeRoot);
        shadowGrid->queryQ(this->treeRoot);
        if(m_useBH){
            this->passBH(true);
            if(this->m_addMultMeta)
                this->produceShootsNew(this->treeRoot,0);
            else
                this->produceShootsOld(this->treeRoot);

        }
        else{
            this->passPriority(true);
            if(this->m_addMultMeta)
                this->produceShootsNew(this->treeRoot,0);
            else
                this->produceShootsOld(this->treeRoot);
        }

        this->calculateBranchDiameter(this->treeRoot);
        delete m_mesh;
        m_mesh = NULL;
        m_mesh = new TreeMesh(this, render);
    }
}

void TreeGen3D::iterateSpaceColD(SpaceCol3D * spaceCol)
{
   // for (int i = 0; i < 10; i++)
    {
        spaceCol->assignMarkersB(this->treeRoot);
        spaceCol->populateBudB(this->treeRoot,0);
        if(m_useBH){
            this->passBH(false);
            if(this->m_addMultMeta)
                this->produceShootsNew(this->treeRoot,0);
            else
                this->produceShootsOld(this->treeRoot);

        }
        else{
            this->passPriority(false);
            if(this->m_addMultMeta)
                this->produceShootsNew(this->treeRoot,0);
            else
                this->produceShootsOld(this->treeRoot);
        }

        this->calculateBranchDiameter(this->treeRoot);
        delete m_mesh;
        m_mesh = NULL;
        m_mesh = new TreeMesh(this, render);
    }
}

TreeGen3D* TreeGen3D::arrayToTree(vector <float *> v)
{
    int idxArr[v.size()];
    idxArr[0] = -1;
    //vector <Bud*> budAdd;

    TreeGen3D * tree = new TreeGen3D();
    int l = v.size();
    float * Array = v.at(0);
    // load parameters:

    tree->m_nextIndx = Array[0];
    tree->in_interact = Array[1] ;
    tree->m_addMultMeta = Array[2] ;
    tree->m_apicalControl = Array[3] ;
    tree->m_useBH  = Array[4];
    tree->LAMBDA  = Array[5];
    tree->ALPHA  = Array[6];
    tree->DIAMETER_RATE  = Array[7];
    tree->TROPISM  = Array[8];
    tree->AUX_ANGLE  = Array[9];
    tree->AUX_WEIGHT  = Array[10];
    tree->TERM_WEIGHT  = Array[11];
    tree->TLENGTH  = Array[12];
    tree->ALENGTH  = Array[13];
    tree->SHED_THRES  = Array[14];
    tree->W_MAX  = Array[15];
    tree->W_MIN  = Array[16];
    tree->KAPPA  = Array[17];
    tree->numBuds  = Array[18];
    Bud * budAdd[tree->numBuds];
    // initialize root:
    Array = v.at(1);
    Bud * bud = tree->treeRoot;
    cout << "treeroot:" << bud << endl;
    bud->term_fate = (Fate)Array [1];
    bud->aux_fate = (Fate)Array[2];
    bud->budsAbove = Array[ 3];
    bud->exposure = Array[ 4];
    bud->darken = Array[ 5] ;
    bud->rotleaf = Array[ 6];
    bud->season = Array[ 7];
    bud->direction.x = Array[ 8];
    bud->direction.y = Array[ 9];
    bud->direction.z = Array[ 10];
    bud->diameter = Array[ 11];
    bud->length = Array[ 12];
    bud->phi = Array[ 13];
    bud->theta = Array[ 14];
    bud->start.x = Array[ 15];
    bud->start.y = Array[ 16];
    bud->start.z = Array[ 17];
    if ( Array[18] > 0.5)
        bud->leaf = true;
    else
        bud->leaf = false;
    bud->i = Array[ 19];
    bud->j = Array[ 20];
    bud->k = Array[ 21];
    bud->tQ = Array[ 22];
    bud->aQ = Array[ 23] ;
    bud->iQ = Array[ 24];
    bud->resource = Array[ 25];
    bud->ares = Array[ 26];
    bud->tres = Array[ 27];
    bud->auxAngle = Array[ 28];
    bud->auxDir.x = Array[ 29];
    bud->auxDir.y = Array[ 30];
    bud->auxDir.z = Array[ 31];
    bud->tV.x = Array[ 32];
    bud->tV.y = Array[ 33];
    bud->tV.z = Array[ 34];
    bud->aV.x = Array[ 35];
    bud->aV.y = Array[ 36];
    bud->aV.z = Array[ 37];
    bud->spaceColIndex = Array[ 38];
    bud->end.x = Array[ 39];
    bud->end.y = Array[40];
    bud->end.z = Array[41];
    bud->aux = NULL;
    bud->term = NULL;
    //idxArr[1] = Array[0];
    //cout << "rootIdx:" << idxArr[1] << endl;
    budAdd[1] = bud;

    for (int i = 2; i <= tree->numBuds; i++)
    {
        budAdd[i] = NULL;
    }
    if (Array[42] > 0)
    {
        if (!budAdd[(int)Array[42]]) budAdd[(int)Array[42]] = new Bud();
        bud->term = budAdd[(int)Array[42]];
    }
    if (Array[43] > 0)
    {
        if (!budAdd[(int)Array[43]]) budAdd[(int)Array[43]] = new Bud();
        bud->aux = budAdd[(int)Array[43]];
    }

    for (int i = 2; i < l; i++)
    {
        float * Array = v.at(i);
        //bud = new Bud();
        if (!budAdd[(int)Array[38]]) budAdd[(int)Array[38]] = new Bud();
        bud = budAdd[(int)Array[38]];
        bud->term_fate = (Fate)Array [1];
        bud->aux_fate = (Fate)Array[2];
        bud->budsAbove = Array[ 3];
        bud->exposure = Array[ 4];
        bud->darken = Array[ 5] ;
        bud->rotleaf = Array[ 6];
        bud->season = Array[ 7];
        bud->direction.x = Array[ 8];
        bud->direction.y = Array[ 9];
        bud->direction.z = Array[ 10];
        bud->diameter = Array[ 11];
        bud->length = Array[ 12];
        bud->phi = Array[ 13];
        bud->theta = Array[ 14];
        bud->start.x = Array[ 15];
        bud->start.y = Array[ 16];
        bud->start.z = Array[ 17];
        if ( Array[18] > 0.5)
            bud->leaf = true;
        else
            bud->leaf = false;
        bud->i = Array[ 19];
        bud->j = Array[ 20];
        bud->k = Array[ 21];
        bud->tQ = Array[ 22];
        bud->aQ = Array[ 23] ;
        bud->iQ = Array[ 24];
        bud->resource = Array[ 25];
        bud->ares = Array[ 26];
        bud->tres = Array[ 27];
        bud->auxAngle = Array[ 28];
        bud->auxDir.x = Array[ 29];
        bud->auxDir.y = Array[ 30];
        bud->auxDir.z = Array[ 31];
        bud->tV.x = Array[ 32];
        bud->tV.y = Array[ 33];
        bud->tV.z = Array[ 34];
        bud->aV.x = Array[ 35];
        bud->aV.y = Array[ 36];
        bud->aV.z = Array[ 37];
        bud->spaceColIndex = Array[ 38];
        bud->end.x = Array[ 39];
        bud->end.y = Array[40];
        bud->end.z = Array[41];
        bud->aux = NULL;
        bud->term = NULL;
        if (Array[42] > 0)
        {
            if (!budAdd[(int)Array[42]]) budAdd[(int)Array[42]] = new Bud();
            bud->term = budAdd[(int)Array[42]];
        }
        if (Array[43] > 0)
        {
            if (!budAdd[(int)Array[43]]) budAdd[(int)Array[43]] = new Bud();
            bud->aux = budAdd[(int)Array[43]];
        }

        /*idxArr[i] = Array[0];
       // cout << "currentIx:" << idxArr[i] << endl;
        budAdd[i] = ( bud);

        if (idxArr[i] % 2 != 0)
        {
            int parent = (idxArr[i] - 1) / 2;
            if (parent >= 0)
            {
                for (int j = 0; j < i; j++)
                {
                    if (idxArr[j] == parent)
                    {
                        Bud * parent = budAdd[j];
                        parent->term  =  bud;
                       // cout << "add term." << idxArr[j] << "---" << idxArr[i] << parent << "---" << bud <<  endl;
                    }
                }
            }
        }
        else
        {
            int parent = (idxArr[i] - 2) / 2;
            if (parent >= 0)
            {
                for (int j = 0; j < i; j++)
                {
                    if (idxArr[j] == parent)
                    {

                        Bud * parent = budAdd[j];
                        parent->aux  =  bud;
                       // cout << "add aux." << idxArr[j] << "---" << idxArr[i] << parent << "---" << bud <<  endl;
                    }
                }
            }
        }*/
    }

    //tree->numBuds = l - 1;
    if (tree->treeRoot->aux == NULL || tree->treeRoot->term == NULL)
    {
        cout << "NULL tree root" << endl;
        //return NULL;
    }
   // cout << "finished" << endl;
    return tree;
}


TreeGen3D * TreeGen3D::loadTreeFromFile( QString filename)
{
    vector <float*> arr = readFromFile(filename);
    if (arr.size() == 0)
        return NULL;
    TreeGen3D * tree = arrayToTree(arr);
    return tree;
}
